package com.eight.cloud.job.quartz;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.eight.cloud.core.global.constant.UserTypeEnum;
import com.eight.cloud.core.global.model.JobUser;
import com.eight.cloud.core.utils.supply.UserContext;
import com.eight.cloud.job.common.constants.ScheduleConstants;
import com.eight.cloud.job.entity.JobLogEntity;
import com.eight.cloud.job.model.vo.JobDataMapVo;
import com.eight.cloud.job.service.IJobExecutionLogService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.BeanUtils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * @ClassName: AbstractQuartzJob
 * @Author: TXC
 * @Date: 2025-02-24 18:33
 **/
@Slf4j
public abstract class AbstractQuartzJob implements Job {
    /**
     * 线程本地变量
     */
    private static final ThreadLocal<LocalDateTime> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMapVo jobDataMap = new JobDataMapVo();
        BeanUtils.copyProperties(context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES), jobDataMap);
        try {
            before(context, jobDataMap);
            doExecute(context, jobDataMap);
            after(context, jobDataMap, null);
        } catch (Exception e) {
            log.error("任务执行异常  - ：", e);
            after(context, jobDataMap, e);
        }
    }

    /**
     * 执行前
     *
     * @param context    工作执行上下文对象
     * @param jobDataMap 系统计划任务
     */
    protected void before(JobExecutionContext context, JobDataMapVo jobDataMap) {
        threadLocal.set(LocalDateTime.now());
        UserContext.setUser(getJobUser(jobDataMap));
    }

    /**
     * 执行后
     *
     * @param context    工作执行上下文对象
     * @param jobDataMap 系统计划任务
     */
    protected void after(JobExecutionContext context, JobDataMapVo jobDataMap, Exception e) {
        try {
            LocalDateTime startTime = threadLocal.get();
            threadLocal.remove();
            JobLogEntity jobLog = new JobLogEntity();
            jobLog.setJobId(jobDataMap.getJobId());
            jobLog.setTenantId(jobDataMap.getTenantId());
            jobLog.setJobName(jobDataMap.getJobName());
            jobLog.setJobGroup(jobDataMap.getJobGroup());
            jobLog.setInvokeTarget(jobDataMap.getInvokeTarget());
            jobLog.setStartTime(startTime);
            jobLog.setStopTime(LocalDateTime.now());
            long runMs = Duration.between(jobLog.getStartTime(), jobLog.getStopTime()).toMillis();
            jobLog.setJobMessage(jobLog.getJobName() + " 总共耗时：" + runMs + "毫秒");
            if (e != null) {
                jobLog.setStatus(1);
                String errorMsg = StrUtil.sub(getExceptionMessage(e), 0, 2000);
                jobLog.setExceptionInfo(errorMsg);
            } else {
                jobLog.setStatus(0);
            }
            // 写入数据库当中
            SpringUtil.getBean(IJobExecutionLogService.class).save(jobLog);
            UserContext.clear();
        }catch (Exception exception) {
            UserContext.clear();
            log.error(exception.getMessage(), exception);
        }

    }

    public static String getExceptionMessage(Throwable e) {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw, true));
        return sw.toString();
    }


    /**
     * 执行方法，由子类重载
     *
     * @param context    工作执行上下文对象
     * @param jobDataMap 系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, JobDataMapVo jobDataMap) throws Exception;

    protected JobUser getJobUser(JobDataMapVo jobDataMap) {
        JobUser jobUser = new JobUser();
        jobUser.setUuid(jobDataMap.getJobId());
        jobUser.setTenantId(jobDataMap.getTenantId());
        jobUser.setUserType(UserTypeEnum.JOB);
        jobUser.setDeptId(jobDataMap.getDeptId());
        return jobUser;
    }
}
